#![allow(non_snake_case)]
use super::{
    bottons::ButtonProp,
    edit_row_state::{EditColumn, EditRowState},
};
use crate::components::crud::EditMode;
use chrono::{Local, NaiveDate, NaiveTime,Datelike};
use evalexpr::ContextWithMutableVariables;
use icondata as i;
use leptos::{prelude::*, task::spawn_local};
use leptos_icons::*;
use serde_json::{Map, Value};
use shq_common::prelude::{
    options::{FetchParams, Options},
    Condition, ControlType, DataType, Layout, Position, Size,
};
use shq_web_controls::{
    use_message, AutoSearch, Checkbox, CheckboxGroup, CheckboxItem, DatePicker, Input, InputNumber, InputPrefix, InputSuffix, MessageOptions, MessageVariant, MonthPicker, MultiSelect, Popover, PopoverTrigger, QuarterPicker, RadioGroup, RadioItem, Select, SelectOption, TextArea, TimePicker, TreeItems, TreeSelect, YearPicker
};
use std::collections::{HashMap, HashSet};
use thaw_utils::{ArcOneCallback, BoxOneCallback};
#[component]
pub fn DataEdit(
    menu_id: u32,
    #[prop(default=None)] title: Option<String>,
    #[prop(default = 660, into)] max_width: u16,
    #[prop(default=Position::Modal,into)] position: Position,
    #[prop(default=Layout::Vertical,into)] layout: Layout,
    #[prop(into)] field_props: StoredValue<Vec<EditColumn>>,
    #[prop(optional, into)] row_data: RwSignal<Option<Value>>,
    #[prop(into)] edit_mode: RwSignal<EditMode>,
    #[prop(default=None)] conf_name: Option<String>,
    #[prop(optional, into)] botton_props: Vec<ButtonProp>,
    #[prop(default="id".into())] pk_col_name: Oco<'static, str>,
    #[prop(default="确定".into())] ok_butt_name: Oco<'static, str>,
) -> impl IntoView {
    let mess = use_message();
    let layout = StoredValue::new(layout);
    let position = StoredValue::new(position);
    let conf_name = StoredValue::new(conf_name);
    let botton_props = StoredValue::new(botton_props);
    let ok_butt_name = StoredValue::new(ok_butt_name.to_string());
    let init_edit_row_data = move |rd: Option<&Value>| {
        if let Some(row_data) = rd {
            EditRowState::from_row_data(row_data)
        } else {
            field_props.with_value(|cols| EditRowState::from_columns(cols, vec![]))
        }
    };
    let edit_row_data = RwSignal::new(row_data.with_untracked(|r| init_edit_row_data(r.as_ref())));
    Effect::new(move |_| {
        let erd = row_data.with(|r| init_edit_row_data(r.as_ref()));
        edit_row_data.set(erd);
    });
    let pk_col_name = StoredValue::new(pk_col_name.to_string());
    let save_data = move || {
        let mut row_data = serde_json::Map::new();
        let mut rules_map = HashMap::new();
        let mut is_ok = true;
        field_props.with_value(|cols| {
            for col in cols.iter() {
                let val = edit_row_data
                    .with(|vals| vals.get_value(col.name.as_str()).map(|val| val.get()));
                if let Some(val) = val {
                    let err_msg=if col.edit_props.required && (val.is_null()) {
                        Some("不能为空".to_string())
                    }else if let Some((min,_))=col.edit_props.range_rule{
                        match &val {
                            Value::Number(number) => {
                                if number.is_f64() {
                                    if number.as_f64().unwrap_or(0.0) < min as f64 {
                                        Some(format!("值不能小于{}", min))
                                    } else {
                                        None
                                    }
                                } else if number.is_i64() {
                                    if number.as_i64().unwrap_or(0) < min as i64 {
                                        Some(format!("值不能小于{}", min))
                                    } else {
                                        None
                                    }
                                } else {
                                    None
                                }
                            },
                            Value::String(sv) => {
                                match col.data_type {
                                    DataType::String => {
                                        if sv.len() < min as usize {
                                            Some(format!("字数不能小于{}", min))
                                        } else {
                                            None
                                        }
                                    },
                                    DataType::Date | DataType::Datetime=> {
                                        let fmt= if col.data_type == DataType::Date {
                                            "%Y-%m-%d"
                                        } else {
                                            "%Y-%m-%d %H:%M:%S"
                                        };
                                        if let Ok(date) = NaiveDate::parse_from_str(&sv, fmt) {
                                            if date.year() < min as i32 {
                                                Some(format!("年份不能小于{}", min))
                                            } else {
                                                None
                                            }
                                        } else {
                                            Some("日期格式不正确".to_string())
                                        }
                                    },
                                    _ => None,
                                }
                            },
                            _ => None,
                        }
                    }else{
                        None
                    };
                    if let Some(err_msg) = err_msg {
                        edit_row_data.with(|s| {
                            let val = s.get_err_msg(col.name.as_str()).unwrap();
                            is_ok = false;
                            val.set(Some(err_msg));
                        });
                    }
                    row_data.insert(col.name.clone(), val);
                    if let Some(regex) = col.edit_props.regex_rule.as_ref() {
                        rules_map.insert(col.name.clone(), regex.clone());
                    }
                }
            }
        });
        if is_ok {
            if let Some(conf_name) = conf_name.get_value() {
                let is_new = edit_mode.with(|s| *s == EditMode::Add);
                if !is_new {
                    pk_col_name.with_value(|pk| {
                        let val = edit_row_data
                            .with(|vals| vals.get_value(pk).map(|val| val.get()))
                            .expect("主键值不能为空");
                        row_data.insert(pk.clone(), val);
                    });
                };
                let row_data_str = serde_json::to_string(&row_data).unwrap();
                let rules_map = serde_json::to_string(&rules_map).unwrap();
                spawn_local(async move {
                    let res =
                        save_form_data(menu_id, conf_name, row_data_str, rules_map, is_new).await;
                    match res {
                        Ok(pk_id) => {
                            if is_new {
                                edit_row_data.update(|s| {
                                    let pk_col_name = pk_col_name.get_value();
                                    s.set_value(pk_col_name.as_str(),pk_id.into());
                                });
                                edit_mode.set(EditMode::Edit);
                            }
                            mess.create(
                                "保存成功！".to_string(),
                                MessageVariant::Success,
                                MessageOptions::default(),
                            );
                        }
                        Err(err) => {
                            let err_msg = err.to_string();
                            if err_msg.contains(|p| p == '|' && p == ';') {
                                let msgs: Vec<&str> =
                                    err_msg.split(';').filter(|s| !s.is_empty()).collect();
                                edit_row_data.with(|s| {
                                    for msg in msgs.into_iter() {
                                        let idx = msg.find('|').unwrap();
                                        let (col_name, err_msg) = msg.split_at(idx);
                                        if let Some(msg) = s.get_err_msg(col_name) {
                                            msg.set(Some(err_msg.to_string()));
                                        }
                                    }
                                });
                            } else {
                                mess.create(
                                    format!("保存数据失败：{}", err),
                                    MessageVariant::Error,
                                    MessageOptions::default(),
                                )
                            }
                        }
                    }
                })
            }
        }
    };
    let buttons_view = move || {
        let mut props = botton_props.get_value();
        if props.is_empty() {
            props.push(ButtonProp::new(
                ok_butt_name.get_value(),
                i::AiSaveOutlined,
                ArcOneCallback::new(move |_| {
                    save_data();
                }),
            ));
            if position.get_value() == Position::Modal {
                props.push(ButtonProp::new(
                    "关闭".to_string(),
                    i::AiCloseCircleOutlined,
                    ArcOneCallback::new(move |_| {
                        edit_mode.set(EditMode::None);
                    }),
                ));
            }
        }
        props
            .into_iter()
            .map(|prop| {
                view! {
                    <button class="btn btn-sm" on:click=move|_|{
                        let vals:Vec<(String,Value)>=edit_row_data.with(|s| {
                            s.iter().map(|v| {
                                (v.name.to_string(), v.value.get())
                            }).collect::<Vec<_>>()
                        });
                        (prop.on_click)(vals);
                    }>
                        <Icon icon=prop.icon/>
                        {prop.text.clone()}
                    </button>
                }
            })
            .collect::<Vec<_>>()
    };
    let layout_view = move || {
        field_props.with_value(|cols| {
            let mut row_indexs: Vec<Vec<usize>> = Vec::new();
            let mut row_index = 0_usize;
            let mut gsum = 0_u8;
            for (i, col) in cols.iter().enumerate() {
                if gsum == 0 || gsum + col.edit_props.grid > 12 {
                    row_indexs.push(vec![i]);
                    if gsum > 0 {
                        row_index += 1
                    };
                    gsum = col.edit_props.grid;
                } else {
                    gsum = gsum + col.edit_props.grid;
                    row_indexs[row_index].push(i);
                }
            }
            let rlen=row_indexs.len();
            row_indexs.iter().map(|row| {
                view! {
                    <div class="flex flex-row gap-4">
                        {
                            row.iter().map(|i|{
                                let c = &cols[*i];
                                let val = edit_row_data.with(|v|v.get_value(c.name.as_str()).map(|v|v.clone())).unwrap();
                                let err_msg = edit_row_data.with(|v|v.get_err_msg(c.name.as_str()).map(|v|v.clone())).unwrap();
                                let layout=layout.get_value();
                                let res=if let Some(dis)=c.edit_props.disabled.as_ref() {
                                    parse_condition(dis, edit_row_data.read_only())
                                }else{
                                    Ok(false)
                                };
                                match res {
                                    Ok(disabled) => {
                                        let col_span=c.edit_props.grid;
                                        let col=StoredValue::new(c.clone());
                                        let inner_max_width=if rlen==1 && layout == Layout::Horizontal {
                                            col_span as u32*15
                                        }else{
                                            0
                                        };
                                        let width=col_span as f64/12.0*100.0;
                                        let width_style=format!("width:{}%",width);
                                        either_of::Either::Left(view! {
                                            <div class="flex-1" style=width_style >
                                                <ControlLayout col disabled val layout err_msg=err_msg.read_only() inner_max_width/>
                                            </div>
                                        })
                                    },
                                    Err(err) => either_of::Either::Right(view! { <p class="text-red-500">{err.to_string()}</p> }),
                                }
                            }).collect_view()
                        }
                        {
                            if rlen==1 && layout.get_value() == Layout::Horizontal {
                                Some(buttons_view)
                            } else {
                                None
                            }
                        }
                    </div>
                }
            }).collect_view()
        })
    };
    let title = StoredValue::new(title);
    view! {
        {
            move || {
                if position.get_value() == Position::Modal {
                    view!{
                        <div id="edit_modal" class="modal" class:modal-open=move||edit_mode.with(|m|*m!=EditMode::None)>
                            <div class="modal-box overflow-hidden" style:max-width=move || format!("{}px", max_width)>
                                <div class="flex items-center justify-between mb-4 rounded-t dark:border-gray-600">
                                    <h3 class="text-xl font-medium text-gray-900 dark:text-white">
                                        {title.get_value()}
                                    </h3>
                                    <button type="button" on:click=move |_| {edit_mode.set(EditMode::None);}
                                        class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white">
                                        <Icon icon=i::AiCloseCircleOutlined width="20px" height="20px"/>
                                    </button>
                                </div>
                                <Show
                                    when=move || { layout.get_value() == Layout::Horizontal }
                                    fallback=move || layout_view
                                >
                                    <div class="flex flex-col gap-4">
                                        {layout_view}
                                    </div>
                                </Show>
                                <div class="modal-action">
                                    {buttons_view}
                                </div>
                            </div>
                        </div>
                    }.into_any()
                } else {
                    view!{
                        <div class="bg-base-100" class:hidden=move||edit_mode.with(|m|*m==EditMode::None)&&position.get_value()!=Position::Inner>
                            <div class="flex flex-col gap-4">
                                {layout_view}
                            </div>
                        </div>
                    }.into_any()
                }
            }
        }
    }
}
#[component]
pub(super) fn ControlLayout(
    #[prop(into)] col: StoredValue<EditColumn>,
    #[prop(default = false, into)] disabled: bool,
    #[prop(into)] val: RwSignal<Value>,
    #[prop(default=Layout::Vertical,into)] layout: Layout,
    #[prop(into)] err_msg: ReadSignal<Option<String>>,
    inner_max_width: u32,
) -> impl IntoView {
    let label = StoredValue::new(col.with_value(|c| c.label.clone()));
    let layout = StoredValue::new(layout);
    let (range, tip) = col.with_value(|c| {
        let range = if let Some((min, max)) = c.edit_props.range_rule {
            Some((min, max))
        } else {
            None
        };
        let tip = if let Some(tip) = c.edit_props.tooltip.as_ref() {
            Some(tip.to_string())
        } else {
            if let Some((min, max)) = range {
                if let DataType::String = c.data_type {
                    let mut msg = "字数".to_string();
                    if min > 0 {
                        msg.push_str("从");
                        msg.push_str(min.to_string().as_str());
                        msg.push_str("到")
                    } else {
                        msg.push_str("小于等于")
                    }
                    if max > 0 {
                        msg.push_str(max.to_string().as_str());
                        msg.push_str(";")
                    }
                    Some(msg)
                } else {
                    Some(format!("取值范围:{}-{}", min, max))
                }
            } else {
                None
            }
        };
        (range, StoredValue::new(tip))
    });
    let cont_view = move || {
        let fetch_params = Signal::derive(move || {
            col.with_value(|c| {
                c.edit_props
                    .options
                    .as_ref()
                    .map(|o| {
                        if let Options::Fetch(fp) = o {
                            Some(fp.clone())
                        } else {
                            None
                        }
                    })
                    .unwrap_or(None)
            })
        });
        let placeholder =
            Signal::derive(move || col.with_value(|c| c.edit_props.placeholder.clone()));
        let label2 = Signal::derive(move || {
            if layout.get_value() == Layout::Horizontal {
                Some(label.get_value())
            } else {
                None
            }
        });
        col.with_value(|c| {
            let cont_type = c.edit_props.control_type;
            let multiple = c.edit_props.multiple;
            let size = c.edit_props.size.clone();
            let opt_conf = c.edit_props.options.as_ref();
            get_control(
                cont_type,
                val,
                multiple,
                size,
                disabled,
                placeholder,
                opt_conf,
                fetch_params,
                range,
                label2,
                err_msg,
                inner_max_width,
            )
        })
    };
    view! {
        <Show
            when=move || { layout.get_value() == Layout::Vertical }
            fallback=move || view!{
                <>{cont_view}</>
            }
        >
            <label class="form-control w-full max-w-xs">
                <div class="label">
                    <span class="label-text">{label.get_value()}</span>
                    {tip.get_value().map(|t|view! {
                        <span class="label-text-alt">{t}</span>
                    })}
                </div>
                {cont_view}
                {move ||err_msg.get().map(|msg|view! {
                    <div class="label">
                        <span class="label-text-alt text-red-500">{msg}</span>
                    </div>
                })}
            </label>
        </Show>
    }
}

fn get_control(
    cont_type: ControlType,
    val: RwSignal<Value>,
    multiple: bool,
    size: Size,
    disabled: bool,
    placeholder: Signal<Option<String>>,
    opt_conf: Option<&Options>,
    fetch_params: Signal<Option<FetchParams>>,
    range: Option<(isize, isize)>,
    label: Signal<Option<String>>,
    err_msg: ReadSignal<Option<String>>,
    inner_max_width: u32,
) -> impl IntoView {
    let cont_type = StoredValue::new(cont_type);
    let label_view = move || {
        if let Some(label) = label.get() {
            view! { <span class="text-neutral min-w-6 text-nowrap text-ellipsis">{label}</span> }
                .into()
        } else {
            None
        }
    };
    let err_msg_view = move || {
        if let Some(msg) = err_msg.get() {
            view! { <Popover>
                <PopoverTrigger slot>
                    <div><Icon icon=i::BiErrorCircleRegular style="color: var(--color-red-500);"/></div>
                </PopoverTrigger>
                {msg}
            </Popover>}
            .into()
        } else {
            None
        }
    };
    match cont_type.get_value() {
        ControlType::TextInput | ControlType::Textarea => {
            let sv = val.with_untracked(|v| {
                if *v == Value::Null {
                    None
                } else {
                    Some(json_val_to_string(v))
                }
            });
            let allow_value = ArcOneCallback::new(move |v: String| {
                if v.is_empty() {
                    return true;
                }
                str_in_range(&v, range)
            });
            let on_change = move |v: Option<String>| {
                let jv = v.map(|sv| Value::String(sv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::A(if cont_type.get_value() == ControlType::TextInput {
                either_of::Either::Left(view! {
                    <Input value=sv size disabled allow_value placeholder on_change inner_max_width>
                        <InputPrefix slot>
                            {label_view}
                        </InputPrefix>
                        <InputSuffix slot>
                            {err_msg_view}
                        </InputSuffix>
                    </Input>
                })
            } else {
                either_of::Either::Right(view! {
                    <TextArea value=sv size disabled placeholder on_change/>
                })
            })
        }
        ControlType::IntegerInput => {
            let sv = val.with(|v| {
                if *v == Value::Null {
                    None
                } else {
                    Some(v.as_i64().unwrap_or(0))
                }
            });
            let rg = range.map(|r| (r.0 as i64, r.1 as i64));
            let on_change = ArcOneCallback::new(move |v: Option<i64>| {
                let jv = v.map(|iv| Value::from(iv)).unwrap_or(Value::Null);
                val.set(jv);
            });
            either_of::EitherOf16::B(view! {
                <InputNumber value=sv step=1 size disabled placeholder range=rg on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </InputNumber>
            })
        }
        ControlType::DecimalInput => {
            let sv = val.with_untracked(|v| {
                if *v == Value::Null {
                    None
                } else {
                    Some(v.as_f64().unwrap_or(0.0))
                }
            });
            let rg = range.map(|r| (r.0 as f64, r.1 as f64));
            let on_change = move |v: Option<f64>| {
                let jv = v.map(|fv| Value::from(fv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::C(view! {
                <InputNumber value=sv step=1.0 size disabled placeholder range=rg on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </InputNumber>
            })
        }
        ControlType::AlphaInput | ControlType::AlphanumInput => {
            let sv = val.with_untracked(|v| {
                if *v == Value::Null {
                    None
                } else {
                    Some(json_val_to_string(v))
                }
            });
            let allow_value = ArcOneCallback::new(move |v: String| {
                if v.is_empty() {
                    return true;
                }
                let has_num = cont_type.get_value() == ControlType::AlphanumInput;
                let is_ok = v.chars().all(|c| {
                    if has_num {
                        c.is_ascii_alphanumeric()
                    } else {
                        c.is_ascii_alphabetic()
                    }
                });
                if !is_ok {
                    return false;
                }
                str_in_range(&v, range)
            });
            let on_change = move |v: Option<String>| {
                let jv = v.map(|sv| Value::String(sv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::D(if cont_type.get_value() == ControlType::TextInput {
                either_of::Either::Left(view! {
                <Input value=sv allow_value size disabled placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </Input>})
            } else {
                either_of::Either::Right(view! {
                    <Input value=sv allow_value size disabled placeholder on_change inner_max_width>
                        <InputPrefix slot>
                            {label_view}
                        </InputPrefix>
                        <InputSuffix slot>
                            {err_msg_view}
                        </InputSuffix>
                    </Input>
                })
            })
        }
        ControlType::Year => {
            let iv = val.with_untracked(|v| {
                if *v == Value::Null {
                    None
                } else {
                    let v=if let Value::String(sv) = v {
                        sv.parse::<i32>().ok()
                    } else if v.is_f64() {
                        None
                    } else if v.is_u64() {
                        v.as_u64().map(|v| v as i32)
                    } else if v.is_i64() {
                        v.as_i64().map(|v| v as i32)
                    } else {
                        None
                    }.unwrap_or(Local::now().year());
                    Some(v)
                }
            });
            let on_change = move |v: Option<i32>| {
                let jv = v.map(|iv| Value::from(iv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::E(view! {
                <YearPicker value=iv size disabled placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </YearPicker>
            })
        }
        ControlType::Quarter => {
            let iv = val.with_untracked(|v| {
                if *v == Value::Null {
                    None
                } else {
                    let v=if let Value::String(sv) = v {
                        sv.replace('Q', "").parse::<i32>().ok()
                    } else if v.is_f64() {
                        None
                    } else if v.is_u64() {
                        v.as_u64().map(|v| v as i32)
                    } else if v.is_i64() {
                        v.as_i64().map(|v| v as i32)
                    } else {
                        None
                    }.unwrap_or(Local::now().year());
                    Some(v)
                }
            });
            let on_change = move |v: Option<i32>| {
                let jv = v.map(|iv| Value::from(iv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::F(view! {
                <QuarterPicker value=iv size disabled placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </QuarterPicker>
            })
        }
        ControlType::Month => {
            let iv = val.with_untracked(|v| match v {
                Value::Null => None,
                Value::String(sv) => {
                    let sv = sv.as_str();
                    sv.parse::<i32>().ok()
                }
                Value::Number(sv) => {
                    if sv.is_f64() {
                        None
                    } else if sv.is_u64() {
                        sv.as_u64().map(|v| v as i32)
                    } else if sv.is_i64() {
                        sv.as_i64().map(|v| v as i32)
                    } else {
                        None
                    }
                }
                _ => None,
            });
            let on_change = move |v: Option<i32>| {
                let jv = v.map(|iv| Value::from(iv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::G(view! {
                <MonthPicker value=iv size disabled placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </MonthPicker>
            })
        }
        ControlType::Date => {
            let value = val.with_untracked(|v| {
                if v == &Value::Null {
                    return None;
                }
                let sv = v.as_str().unwrap_or("");
                let dv = if sv.contains('-') {
                    NaiveDate::parse_from_str(sv, "%Y-%m-%d")
                        .unwrap_or(Local::now().naive_local().date())
                } else {
                    NaiveDate::parse_from_str(sv, "%Y%m%d")
                        .unwrap_or(Local::now().naive_local().date())
                };
                Some(dv)
            });
            let on_change = move |v: Option<String>| {
                let jv = v.map(|iv| Value::from(iv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::H(view! {
                <DatePicker value size disabled placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </DatePicker>
            })
        }
        ControlType::Time => {
            let value = val.with_untracked(|v| {
                if v == &Value::Null {
                    return None;
                }
                let sv = v.to_string();
                let tv = NaiveTime::parse_from_str(sv.as_str(), "%H:%M:%S")
                    .unwrap_or(NaiveTime::default());
                Some(tv)
            });
            let on_change = move |v: Option<String>| {
                let jv = v.map(|iv| Value::from(iv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::I(view! {
                <TimePicker value size disabled placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </TimePicker>
            })
        }
        ControlType::Datetime => {
            let value = val.with_untracked(|v| {
                if v == &Value::Null {
                    return None;
                }
                let sv = v.to_string();
                let dv = if sv.contains('-') {
                    NaiveDate::parse_from_str(sv.as_str(), "%Y-%m-%d %H:%M:%S")
                        .unwrap_or(NaiveDate::default())
                } else {
                    NaiveDate::parse_from_str(sv.as_str(), "%Y%m%d %H%M%S")
                        .unwrap_or(NaiveDate::default())
                };
                Some(dv)
            });
            let on_change = move |v: Option<String>| {
                let jv = v.map(|iv| Value::from(iv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            either_of::EitherOf16::J(view! {
                <DatePicker value size disabled placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </DatePicker>
            })
        }
        ControlType::Select => {
            let options: Vec<SelectOption<String>> = get_select_options(opt_conf);
            let sel_view = if multiple {
                let values = val.with_untracked(|v| get_multi_values(v));
                let on_change = move |vals: Vec<String>| {
                    let sv = if vals.is_empty() {
                        Value::Null
                    } else {
                        Value::from(vals.join("|"))
                    };
                    val.set(sv);
                };
                either_of::Either::Left(view! {
                    <MultiSelect values options size disabled placeholder on_change inner_max_width>
                        <InputPrefix slot>
                            {label_view}
                        </InputPrefix>
                        <InputSuffix slot>
                            {err_msg_view}
                        </InputSuffix>
                    </MultiSelect>
                })
            } else {
                let value = val.with_untracked(|v| {
                    if *v == Value::Null {
                        None
                    } else {
                        Some(json_val_to_string(v))
                    }
                });
                let on_change = move |v: Option<String>| {
                    let jv = v.map(|sv| Value::from(sv)).unwrap_or(Value::Null);
                    val.set(jv);
                };
                either_of::Either::Right(view! {
                    <Select value options size disabled placeholder on_change inner_max_width>
                        <InputPrefix slot>
                            {label_view}
                        </InputPrefix>
                        <InputSuffix slot>
                            {err_msg_view}
                        </InputSuffix>
                    </Select>
                })
            };
            either_of::EitherOf16::K(sel_view)
        }
        ControlType::TreeSelect => {
            let value = val.with_untracked(|v| {
                if *v == Value::Null {
                    None
                } else {
                    Some(json_val_to_string(v))
                }
            });
            let on_change = move |v: Option<String>| {
                let jv = v.map(|sv| Value::from(sv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            let tree_items = opt_conf
                .as_deref()
                .and_then(|opts| Some(TreeItems::from_options(opts)))
                .unwrap_or_default();
            either_of::EitherOf16::L(view! {
                <TreeSelect value size disabled tree_items=StoredValue::new(tree_items) placeholder on_change inner_max_width>
                    <InputPrefix slot>
                        {label_view}
                    </InputPrefix>
                    <InputSuffix slot>
                        {err_msg_view}
                    </InputSuffix>
                </TreeSelect>
            })
        }
        ControlType::Checkbox => {
            let value = val.with_untracked(|v| v.as_bool().unwrap_or(false));
            let on_change = move |v: bool| {
                val.set(Value::from(v));
            };
            either_of::EitherOf16::M(view! {
                <Checkbox value size disabled on_change>
                    {label_view}
                </Checkbox>
            })
        }
        ControlType::CheckboxGroup => {
            let values = val.with_untracked(|v| get_multi_values(v));
            let value: HashSet<String> = HashSet::from_iter(values.into_iter());
            let on_change = move |vals: HashSet<String>| {
                let sv = if vals.is_empty() {
                    Value::Null
                } else {
                    let val2 = vals.into_iter().collect::<Vec<String>>();
                    Value::from(val2.join("|"))
                };
                val.set(sv);
            };
            let children = {
                let opts = get_select_options(opt_conf);
                if !opts.is_empty() {
                    let c = opts
                        .into_iter()
                        .map(|v| {
                            let key = v.value.clone();
                            let label = v.label.clone();
                            view! {<CheckboxItem key label size disabled/>}
                        })
                        .collect_view();
                    Some(c)
                } else {
                    None
                }
            };
            either_of::EitherOf16::N(view! {
                <CheckboxGroup value on_change>
                    {children}
                </CheckboxGroup>
            })
        }
        ControlType::RadioGroup => {
            let value = val.with_untracked(|v| {
                if *v == Value::Null {
                    None
                } else {
                    Some(json_val_to_string(v))
                }
            });
            let on_change = move |v: Option<String>| {
                let jv = v.map(|sv| Value::from(sv)).unwrap_or(Value::Null);
                val.set(jv);
            };
            let children = {
                let opts = get_select_options(opt_conf);
                if !opts.is_empty() {
                    let c = opts
                        .into_iter()
                        .map(|v| {
                            let key = v.value;
                            let label = v.label;
                            view! {<RadioItem key size disabled>{label}</RadioItem>}
                        })
                        .collect_view();
                    Some(c)
                } else {
                    None
                }
            };
            either_of::EitherOf16::O(view! {
                <RadioGroup name="and_or" value on_change>
                    {children}
                </RadioGroup>
            })
        }
        ControlType::Search => {
            let value = val.with_untracked(|v| {
                if v == &Value::Null {
                    None
                } else {
                    Some(json_val_to_string(v))
                }
            });
            let on_change = ArcOneCallback::new(move |v: Option<String>| {
                let jv = v.map(|sv| Value::from(sv)).unwrap_or(Value::Null);
                val.set(jv);
            });
            let fetch_params = StoredValue::new(fetch_params.get().unwrap());
            either_of::EitherOf16::P(view! {
                <AutoSearch value fetch_params size disabled placeholder on_change label err_msg inner_max_width/>
            })
        }
    }
}
fn str_in_range(val: &str, range: Option<(isize, isize)>) -> bool {
    if let Some((_min, max)) = range {
        let l = val.chars().count() as isize;
        let max2 = if max < 0 { 0 } else { max };
        if max2 == 0 {
            return true;
        }
        if l > max2 {
            return false;
        }
    }
    true
}
pub fn json_val_to_string(val: &Value) -> String {
    match val {
        Value::Bool(b) => b.to_string(),
        Value::Number(n) => n.to_string(),
        Value::String(s) => s.to_owned(),
        Value::Null => "".to_string(),
        Value::Object(obj) => format!("{:?}", obj),
        Value::Array(arr) => format!("{:?}", arr),
    }
}

fn get_multi_values(val: &Value) -> Vec<String> {
    let mut vals: Vec<String> = Vec::new();
    if val.is_array() {
        let val2 = val.as_array().unwrap();
        val2.iter().for_each(|v| {
            vals.push(v.to_string());
        })
    } else if val.is_string() {
        let val2 = val.as_str().unwrap();
        val2.split('|').into_iter().for_each(|v| {
            vals.push(v.to_string());
        })
    }
    vals
}

pub(super) fn parse_condition(
    cond: &Condition,
    row_data: ReadSignal<EditRowState>,
) -> Result<bool, String> {
    let is_disa = match cond {
        Condition::Bool(b) => Ok(*b),
        Condition::Exp(exp) => {
            let is_ok = row_data.with(|row: &EditRowState| {
                let mut context: evalexpr::HashMapContext = evalexpr::HashMapContext::new();
                for vs in row.iter() {
                    if !exp.contains(vs.name.as_str()) {
                        continue;
                    }
                    let val = vs.value.with(|v| v.to_string());
                    let res = context.set_value(vs.name.clone(), val.into());
                    if let Err(e) = res {
                        return Err(e.to_string());
                    }
                }
                evalexpr::eval_boolean_with_context(exp, &context).or_else(|e| Err(e.to_string()))
            });
            is_ok
        }
    };
    is_disa
}

#[server(SaveFormData, "/api/protect")]
pub async fn save_form_data(
    menu_id: u32,
    conf_name: String,
    form_data: String,
    rules_map: String,
    is_new: bool,
) -> Result<u64, ServerFnError> {
    use crate::auth::get_user_context_with_auth;
    let rules_map: HashMap<String, String> = serde_json::from_str(&rules_map).unwrap();
    let mut msgs: Vec<String> = Vec::new();
    for (k, v) in rules_map.iter() {
        let res = shq_common::prelude::parse_regex(k, v);
        if let Err(e) = res {
            msgs.push(format!("{}|{}", k, e));
        }
    }
    if msgs.len() > 0 {
        return Err(ServerFnError::ServerError(msgs.join(";")));
    }
    let form_data: Map<String, Value> = serde_json::from_str(&form_data).unwrap();
    let at = if is_new {
        shq_common::prelude::AuthType::Insert
    } else {
        shq_common::prelude::AuthType::Update
    };
    let user = get_user_context_with_auth(menu_id, at).await?;
    let ps = shq_pas_backend::PageService::new(&conf_name, user.user_id, menu_id);
    let res = ps.save_row_data(form_data).await;
    match res {
        Ok(id) => Ok(id),
        Err(e) => Err(ServerFnError::ServerError(e.to_string())),
    }
}

fn get_select_options(opt_conf: Option<&Options>) -> Vec<SelectOption<String>> {
    if let Some(opts) = opt_conf {
        match opts {
            Options::Array(items) => items
                .iter()
                .map(|opt| SelectOption::new(opt, opt.to_string()))
                .collect::<Vec<SelectOption<_>>>(),
            Options::Map(kv_items) => kv_items
                .iter()
                .map(|kv| SelectOption::new(kv.label.as_str(), kv.value.to_string()))
                .collect::<Vec<SelectOption<_>>>(),
            Options::Tree(tree_nodes) => tree_nodes
                .iter()
                .map(|node| SelectOption::new(node.title.as_str(), node.key.to_string()))
                .collect::<Vec<SelectOption<_>>>(),
            Options::Fetch(_fetch_params) => vec![],
        }
    } else {
        vec![]
    }
}

//TODO! 控件选项如果是从数据库中静态提取（不依赖于前端的某个参数），应该在在获取配置时一并返回；
// 如果是动态提取（依赖于前端的某个参数）或者自动搜索；则应该在控件中搜索
// if !fetch_param_cols.is_empty() {
//     let fetch_params = serde_json::to_string(&fetch_param_cols).unwrap();
//     let res = get_fetch_options(fetch_params).await;
//     let result = match res {
//         Ok(datas) => {
//             for (k, v) in datas {
//                 opt_map.insert(k, v);
//             }
//             Ok(opt_map)
//         }
//         Err(e) => Err(e.to_string()),
//     };
//     return result;
// }
